use crate::Opt;
use crate::abort_on_drop::AbortOnDropHandle;
use crate::netem_cmd::build_full_command;
use shared::NetemConfig;
use std::future::pending;
use std::process::{ExitCode, ExitStatus, Stdio};
use tokio::process::{Child, Command};
use command_group::{AsyncCommandGroup, AsyncGroupChild};
use tokio::sync::{
    mpsc::{self, UnboundedSender},
    oneshot,
};

pub enum CtrlMsg {
    Active(Vec<NetemConfig>),
    Deactive,
    Quit,
}

pub fn start_server(opt: Opt) -> (AbortOnDropHandle<()>, UnboundedSender<CtrlMsg>) {
    let (tx, rx) = mpsc::unbounded_channel();
    let handle = tokio::spawn(netem_loop(opt, rx));
    (handle.into(), tx)
}

async fn netem_loop(opt: Opt, mut rx: mpsc::UnboundedReceiver<CtrlMsg>) {
    let mut running_cmd: Option<AsyncGroupChild> = None;
    loop {
        tokio::select! {
            Some(msg) = rx.recv() => match msg {
                CtrlMsg::Active(cfgs) => {
                    let cmd = build_full_command(&opt.announce_ip, &opt.build_user_cmd(), &opt.tmp_dir, cfgs);
                    println!("active cmd [{}]", cmd);
                    let child = Command::new("bash")
                        .arg("-c")
                        .arg(cmd)
                        .stdin(Stdio::null())
                        .stdout(Stdio::null())
                        .stderr(Stdio::null())
                        .group()
                        // .kill_on_drop(true)
                        .spawn();
                    if let Err(ref e) = child {
                        println!("start process error {}", e);
                    }
                    if let Some(mut child) = running_cmd.take() {
                        let _ = child.kill();
                    }
                    running_cmd = child.ok();
                    println!("active {}", running_cmd.as_ref().map(|e| e.id()).flatten().unwrap_or_default());
                },
                CtrlMsg::Deactive => {
                    println!("deactive {}", running_cmd.as_ref().map(|e| e.id()).flatten().unwrap_or_default());
                    if let Some(mut child) = running_cmd.take() {
                        let _ = child.kill();
                    }
                },
                CtrlMsg::Quit => {
                    println!("quit");
                    break;
                },
            },
            msg = async {
                println!("wait process start");
                let a = running_cmd.as_mut().unwrap().wait().await;
                println!("wait process ended: {:?}", a);
                a
            }, if running_cmd.is_some() => {
                match msg {
                    Ok(status) => {
                        println!("process exited with status {}", status);
                    },
                    Err(err) => {
                        println!("process wait error: {}", err);
                    },
                }
                running_cmd = None;
            },
            else => {
                println!("netem_loop break");
                break;
            }
        }
    } // loop

    if let Some(mut child) = running_cmd.take() {
        println!("kill {}", child.id().unwrap_or_default());
        let _ = child.kill();
    }
}
